home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / epsilon / structlike.py < prev    next >
Text File  |  2009-03-13  |  6KB  |  184 lines

  1. # -*- test-case-name: epsilon.test.test_structlike -*-
  2.  
  3. """
  4. This module implements convenience objects for classes which have initializers
  5. and repr()s that describe a fixed set of attributes.
  6. """
  7.  
  8. from twisted.python import context
  9.  
  10. _NOT_SPECIFIED = object()
  11.  
  12.  
  13. class _RecursiveReprer(object):
  14.     """
  15.     This object maintains state so that repr()s can tell when they are
  16.     recursing and not do so.
  17.     """
  18.     def __init__(self):
  19.         self.active = {}
  20.  
  21.     def recursiveRepr(self, stuff, thunk=repr):
  22.         """
  23.         Recursive repr().
  24.         """
  25.         ID = id(stuff)
  26.         if ID in self.active:
  27.             return '%s(...)' % (stuff.__class__.__name__,)
  28.         else:
  29.             try:
  30.                 self.active[ID] = stuff
  31.                 return thunk(stuff)
  32.             finally:
  33.                 del self.active[ID]
  34.  
  35.  
  36. def _contextualize(contextFactory, contextReceiver):
  37.     """
  38.     Invoke a callable with an argument derived from the current execution
  39.     context (L{twisted.python.context}), or automatically created if none is
  40.     yet present in the current context.
  41.  
  42.     This function, with a better name and documentation, should probably be
  43.     somewhere in L{twisted.python.context}.  Calling context.get() and
  44.     context.call() individually is perilous because you always have to handle
  45.     the case where the value you're looking for isn't present; this idiom
  46.     forces you to supply some behavior for that case.
  47.  
  48.     @param contextFactory: An object which is both a 0-arg callable and
  49.     hashable; used to look up the value in the context, set the value in the
  50.     context, and create the value (by being called).
  51.  
  52.     @param contextReceiver: A function that receives the value created or
  53.     identified by contextFactory.  It is a 1-arg callable object, called with
  54.     the result of calling the contextFactory, or retrieving the contextFactory
  55.     from the context.
  56.     """
  57.     value = context.get(contextFactory, _NOT_SPECIFIED)
  58.     if value is not _NOT_SPECIFIED:
  59.         return contextReceiver(value)
  60.     else:
  61.         return context.call({contextFactory: contextFactory()},
  62.                             _contextualize, contextFactory, contextReceiver)
  63.  
  64.  
  65.  
  66. class StructBehavior(object):
  67.     __names__ = []
  68.     __defaults__ = []
  69.  
  70.     def __init__(self, *args, **kw):
  71.         super(StructBehavior, self).__init__()
  72.  
  73.         # Turn all the args into kwargs
  74.         if len(args) > len(self.__names__):
  75.             raise TypeError(
  76.                 "Got %d positional arguments but expected no more than %d" %
  77.                 (len(args), len(self.__names__)))
  78.  
  79.         for n, v in zip(self.__names__, args):
  80.             if n in kw:
  81.                 raise TypeError("Got multiple values for argument " + n)
  82.             kw[n] = v
  83.  
  84.         # Fill in defaults
  85.         for n, v in zip(self.__names__[::-1], self.__defaults__[::-1]):
  86.             if n not in kw:
  87.                 kw[n] = v
  88.  
  89.         for n in self.__names__:
  90.             if n not in kw:
  91.                 raise TypeError('Specify a value for %r' % (n,))
  92.             setattr(self, n, kw.pop(n))
  93.  
  94.         if kw:
  95.             raise TypeError('Got unexpected arguments: ' + ', '.join(kw))
  96.  
  97.  
  98.     def __repr__(self):
  99.         """
  100.         Generate a string representation.
  101.         """
  102.  
  103.         def doit(rr):
  104.             def _recordrepr(self2):
  105.                 """
  106.                 Internal implementation of repr() for this record.
  107.                 """
  108.                 return '%s(%s)' % (
  109.                     self.__class__.__name__,
  110.                     ', '.join(["%s=%s" %
  111.                                (n, repr(getattr(self, n, None)))
  112.                                for n in self.__names__]))
  113.             return rr.recursiveRepr(self, _recordrepr)
  114.         return _contextualize(_RecursiveReprer, doit)
  115.  
  116.  
  117. def record(*a, **kw):
  118.     """
  119.     Are you tired of typing class declarations that look like this?
  120.  
  121.         class StuffInfo:
  122.             def __init__(self, a=None, b=None, c=None, d=None, e=None,
  123.                          f=None, g=None, h=None, i=None, j=None):
  124.                 self.a = a
  125.                 self.b = b
  126.                 self.c = c
  127.                 self.d = d
  128.                 # ...
  129.  
  130.     Epsilon can help!  That's right - for a limited time only, this function
  131.     returns a class which provides a shortcut.  The above can be simplified
  132.     to::
  133.  
  134.         StuffInfo = record(a=None, b=None, c=None, d=None, e=None,
  135.                            f=None, g=None, h=None, i=None, j=None)
  136.  
  137.     if the arguments are required, rather than having defaults, it could be
  138.     even shorter:
  139.  
  140.         StuffInfo = record('a b c d e f g h i j')
  141.  
  142.     Put more formally: C{record} optionally takes one positional argument, a
  143.     L{str} representing attribute names as whitespace-separated identifiers; it
  144.     also takes an arbitrary number of keyword arguments, which map attribute
  145.     names to their default values.  If no positional argument is provided, the
  146.     names of attributes will be inferred from the names of the defaults
  147.     instead.
  148.     """
  149.     if len(a) == 1:
  150.         attributeNames = a[0].split()
  151.     elif len(a) == 0:
  152.         if not kw:
  153.             raise TypeError("Attempted to define a record with no attributes.")
  154.         attributeNames = kw.keys()
  155.         attributeNames.sort()
  156.     else:
  157.         raise TypeError(
  158.             "record must be called with zero or one positional arguments")
  159.  
  160.     # Work like Python: allow defaults specified backwards from the end
  161.     defaults = []
  162.     for attributeName in attributeNames:
  163.         default = kw.pop(attributeName, _NOT_SPECIFIED)
  164.         if defaults:
  165.             if default is _NOT_SPECIFIED:
  166.                 raise TypeError(
  167.                     "You must specify default values like in Python; "
  168.                     "backwards from the end of the argument list, "
  169.                     "with no gaps")
  170.             else:
  171.                 defaults.append(default)
  172.         elif default is not _NOT_SPECIFIED:
  173.             defaults.append(default)
  174.         else:
  175.             # This space left intentionally blank.
  176.             pass
  177.     if kw:
  178.         raise TypeError("The following defaults did not apply: %r" % (kw,))
  179.  
  180.     return type('Record<%s>' % (' '.join(attributeNames),),
  181.                 (StructBehavior,),
  182.                 dict(__names__=attributeNames,
  183.                      __defaults__=defaults))
  184.